/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "hos_camera_demo.h"
#include <cstdio>
#include <getopt.h>

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<poll.h>

#include <iostream>
#include <cinttypes>
#include <fstream>

#include <sstream>

#include <sys/stat.h>

#include <sys/types.h>

#define MSG(args...) printf(args) 

namespace OHOS::Camera {
static void Usage(FILE* fp)
{
    fprintf(fp,
            "Options:\n"
            "-h | --help          Print this message\n"
            "-o | --offline       stream offline test\n"
            "-c | --capture       capture one picture\n"
            "-w | --set WB        Set white balance Cloudy\n"
            "-v | --video         capture Viedeo of 10s\n"
            "-a | --Set AE        Set Auto exposure\n"
            "-f | --Set Flashlight        Set flashlight ON 5s OFF\n"
            "-q | --quit          stop preview and quit this app\n");
}

    int gpio_fd, ret;
    struct pollfd fds[1];
    char buff[10];
//函数声明
static int gpio_export(int pin);
static int gpio_unexport(int pin);
static int gpio_direction(int pin, int dir);
static int gpio_write(int pin, int value);
static int gpio_read(int pin);
static int gpio_edge(int pin, int edge);



static int gpio_export(int pin)  
{  
    char buffer[64];  
    int len;  
    int fd;  
  
    fd = open("/sys/class/gpio/export", O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open export for writing!\n");  
        return(-1);  
    }  
  
    len = snprintf(buffer, sizeof(buffer), "%d", pin);  
    
    if (write(fd, buffer, len) < 0) 
    {  
        MSG("Failed to export gpio!");  
        return -1;  
    }  
     
    close(fd);  
    return 0;  
}  
static int gpio_unexport(int pin)  
{  
    char buffer[64];  
    int len;  
    int fd;  
  
    fd = open("/sys/class/gpio/unexport", O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open unexport for writing!\n");  
        return -1;  
    }  
  
    len = snprintf(buffer, sizeof(buffer), "%d", pin);  
    if (write(fd, buffer, len) < 0) 
    {  
        MSG("Failed to unexport gpio!");  
        return -1;  
    }  
     
    close(fd);  
    return 0;  
} 
//dir: 0-->IN, 1-->OUT
static int gpio_direction(int pin, int dir)  
{  
    static const char dir_str[] = "in\0out";  
    char path[64];  
    int fd;  
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/direction", pin);  
    fd = open(path, O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio direction for writing!\n");  
        return -1;  
    }  
  
    if (write(fd, &dir_str[dir == 0 ? 0 : 3], dir == 0 ? 2 : 3) < 0) 
    {  
        MSG("Failed to set direction!\n");  
        return -1;  
    }  
  
    close(fd);  
    return 0;  
}  
//value: 0-->LOW, 1-->HIGH
static int gpio_write(int pin, int value)  
{  
    static const char values_str[] = "01";  
    char path[64];  
    int fd;  
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);  
    fd = open(path, O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio value for writing!\n");  
        return -1;  
    }  
  
    if (write(fd, &values_str[value == 0 ? 0 : 1], 1) < 0) 
    {  
        MSG("Failed to write value!\n");  
        return -1;  
    }  
  
    close(fd);  
    return 0;  
}
static int gpio_read(int pin)  
{  
    char path[64];  
    char value_str[3];  
    int fd;  
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/value", pin);  
    fd = open(path, O_RDONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio value for reading!\n");  
        return -1;  
    }  
  
    if (read(fd, value_str, 3) < 0)
    {  
        MSG("Failed to read value!\n");  
        return -1;  
    }  
  
    close(fd);  
    return (atoi(value_str));
}  
// none表示引脚为输入，不是中断引脚
// rising表示引脚为中断输入，上升沿触发
// falling表示引脚为中断输入，下降沿触发
// both表示引脚为中断输入，边沿触发
// 0-->none, 1-->rising, 2-->falling, 3-->both
static int gpio_edge(int pin, int edge)
{
const char dir_str[] = "none\0rising\0falling\0both"; 
int ptr;
char path[64];  
    int fd; 
switch(edge)
{
    case 0:
        ptr = 0;
        break;
    case 1:
        ptr = 5;
        break;
    case 2:
        ptr = 12;
        break;
    case 3:
        ptr = 20;
        break;
    default:
        ptr = 0;
} 
  
    snprintf(path, sizeof(path), "/sys/class/gpio/gpio%d/edge", pin);  
    fd = open(path, O_WRONLY);  
    if (fd < 0) 
    {  
        MSG("Failed to open gpio edge for writing!\n");  
        return -1;  
    }  
  
    if (write(fd, &dir_str[ptr], strlen(&dir_str[ptr])) < 0) 
    {  
        MSG("Failed to set edge!\n");  
        return -1;  
    }  
  
    close(fd);  
    return 0;  
}

enum class mode_ {
    MODE_PREVIEW = 0,
    MODE_PHOTO
};


const static struct option longOptions[] = {
    {"help", no_argument, nullptr, 'h'}, {"capture", no_argument, nullptr, 'c'},
    {"WB", no_argument, nullptr, 'w'}, {"video", no_argument, nullptr, 'v'},
    {"quit", no_argument, nullptr, 'q'}, {"AE", no_argument, nullptr, 'a'},
    {"OL", no_argument, nullptr, 'o'}, {"flashlight", no_argument, nullptr, 'f'},
    {0, 0, 0, 0}
};

static int PutMenuAndGetChr(void)
{
    constexpr uint32_t inputCount = 50;
    int c = 0;
    char strs[inputCount];

    Usage(stdout);
    CAMERA_LOGD("pls input command(input -q exit this app)\n");
    fgets(strs, inputCount, stdin);

    for (int i = 0; i < inputCount; i++) {
        if (strs[i] != '-') {
            c = strs[i];
            break;
        }
    }

    return c;
}

static RetCode PreviewOn(int mode, const std::shared_ptr<HosCameraDemo>& mainDemo)
{
    RetCode rc = RC_OK;
    CAMERA_LOGD("main test: PreviewOn enter");

    rc = mainDemo->StartPreviewStream();
    if (rc != RC_OK) {
        CAMERA_LOGE("main test: PreviewOn StartPreviewStream error");
        return RC_ERROR;
    }

    if (mode == 0) {
        rc = mainDemo->StartCaptureStream();
        if (rc != RC_OK) {
            CAMERA_LOGE("main test: PreviewOn StartCaptureStream error");
            return RC_ERROR;
        }
    } else {
        rc = mainDemo->StartVideoStream();
        if (rc != RC_OK) {
            CAMERA_LOGE("main test: PreviewOn StartVideoStream error");
            return RC_ERROR;
        }
    }

    rc = mainDemo->CaptureON(STREAM_ID_PREVIEW, CAPTURE_ID_PREVIEW, CAPTURE_PREVIEW);
    if (rc != RC_OK) {
        CAMERA_LOGE("main test: PreviewOn mainDemo->CaptureON() preview error");
        return RC_ERROR;
    }

    CAMERA_LOGD("main test: PreviewOn exit");
    return RC_OK;
}

static void PreviewOff(const std::shared_ptr<HosCameraDemo>& mainDemo)
{
    CAMERA_LOGD("main test: PreviewOff enter");

    mainDemo->CaptureOff(CAPTURE_ID_PREVIEW, CAPTURE_PREVIEW);
    mainDemo->ReleaseAllStream();

    CAMERA_LOGD("main test: PreviewOff exit");
}

static void FlashLightTest(const std::shared_ptr<HosCameraDemo>& mainDemo)
{
    constexpr size_t delayTime = 5;

    PreviewOff(mainDemo);
    mainDemo->ReleaseCameraDevice();
    sleep(1);
    mainDemo->FlashlightOnOff(true);
    sleep(delayTime);
    mainDemo->FlashlightOnOff(false);
    mainDemo->InitCameraDevice();
    PreviewOn(0, mainDemo);
}

static void OfflineTest(const std::shared_ptr<HosCameraDemo>& mainDemo)
{
    RetCode rc = RC_OK;

    PreviewOff(mainDemo);

    mainDemo->StartDualStreams(STREAM_ID_CAPTURE);
    mainDemo->CaptureOnDualStreams(STREAM_ID_CAPTURE);
    sleep(1);

    rc = mainDemo->StreamOffline(STREAM_ID_CAPTURE);
    if (rc != RC_OK) {
        CAMERA_LOGE("main test: mainDemo->StreamOffline error");
    } else {
        sleep(1);
        mainDemo->InitCameraDevice();
        rc = PreviewOn(0, mainDemo);
        if (rc != RC_OK) {
            CAMERA_LOGE("main test: PreviewOn() error");
        }
    }
}

static void CaptureTest(const std::shared_ptr<HosCameraDemo>& mainDemo)
{
    RetCode rc = RC_OK;
    constexpr size_t delayTime = 5;

    rc = mainDemo->CaptureON(STREAM_ID_CAPTURE, CAPTURE_ID_CAPTURE, CAPTURE_SNAPSHOT);
    if (rc != RC_OK) {
        CAMERA_LOGE("main test: mainDemo->CaptureON() capture error");
        return;
    }

    sleep(delayTime);
    rc = mainDemo->CaptureOff(CAPTURE_ID_CAPTURE, CAPTURE_SNAPSHOT);
    if (rc != RC_OK) {
        CAMERA_LOGE("main test: mainDemo->CaptureOff() capture error");
        return;
    }
}

static void VideoTest(const std::shared_ptr<HosCameraDemo>& mainDemo)
{
    RetCode rc = RC_OK;
    constexpr size_t delayTime = 5;

    PreviewOff(mainDemo);
    mainDemo->StartDualStreams(STREAM_ID_VIDEO);
    mainDemo->CaptureOnDualStreams(STREAM_ID_VIDEO);

    sleep(delayTime);
    mainDemo->CaptureOff(CAPTURE_ID_PREVIEW, CAPTURE_PREVIEW);
    mainDemo->CaptureOff(CAPTURE_ID_VIDEO, CAPTURE_VIDEO);
    mainDemo->ReleaseAllStream();

    rc = PreviewOn(0, mainDemo);
    if (rc != RC_OK) {
        CAMERA_LOGE("main test: video PreviewOn() error plese -q exit demo");
    }
}

static void ManuList(const std::shared_ptr<HosCameraDemo>& mainDemo,
    const int argc, char** argv)
{
    int idx, c;
    int awb = 1;
    constexpr char shortOptions[] = "h:cwvaqof:";

    c = getopt_long(argc, argv, shortOptions, longOptions, &idx);
    while(1) {
                ret = poll(fds,1,5000);
		if( ret == -1 )
		MSG("poll\n");
		if( fds[0].revents & POLLPRI)
		{
		    ret = lseek(gpio_fd,0,SEEK_SET);
		    if( ret == -1 )
		    MSG("lseek\n");
	 
		     ret = read(gpio_fd,buff,10);//读取按钮值，但这里没使用
		    if( ret == -1 )
		    MSG("read\n");
	 
		    //切换红灯
		    int status = gpio_read(41);
		    printf("41 = %d\n",status);
		    gpio_write(41, 1 - status);
	 
		    //11.拍摄照片。
		    CaptureTest(mainDemo);
		    printf("*****************Success to key_capture!*****************\n");
                    c = PutMenuAndGetChr();
	            break;
		
		}

        switch (c) {
            case 'h':
                c = PutMenuAndGetChr();
                break;

            case 'f':
                FlashLightTest(mainDemo);
                c = PutMenuAndGetChr();
                break;

            case 'o':
                OfflineTest(mainDemo);
                c = PutMenuAndGetChr();
                break;

            case 'c':
                CaptureTest(mainDemo);
                c = PutMenuAndGetChr();
                break;

            case 'w':
                if (awb) {
                    mainDemo->SetAwbMode(OHOS_CAMERA_AWB_MODE_INCANDESCENT);
                } else {
                    mainDemo->SetAwbMode(OHOS_CAMERA_AWB_MODE_OFF);
                }
                awb = !awb;
                c = PutMenuAndGetChr();
                break;

            case 'a':
                mainDemo->SetAeExpo();
                c = PutMenuAndGetChr();
                break;

            case 'v':
                VideoTest(mainDemo);
                c = PutMenuAndGetChr();
                break;
                
            case 'q':
                PreviewOff(mainDemo);
                mainDemo->QuitDemo();
                return;

            default:
                CAMERA_LOGE("main test: command error please retry input command");
                c = PutMenuAndGetChr();
                break;
        }
    }
}

int main(int argc, char** argv)
{

    //41为红灯, 1为1号按键
 
    gpio_unexport(41);
    gpio_unexport(1);
 
    
    //41红灯亮起
    gpio_export(41);
    gpio_direction(41, 1);//output out
    gpio_write(41, 1);
    
    //1按钮初始化
    gpio_export(1);
    gpio_direction(1, 0);//input in
    gpio_edge(1,2);
    gpio_fd = open("/sys/class/gpio/gpio1/value",O_RDONLY);
    if(gpio_fd < 0)
    {
        MSG("Failed to open value!\n");  
        return -1;  
    }
    fds[0].fd = gpio_fd;
    fds[0].events  = POLLPRI;

    // 参考Camera组件拍摄 https://gitee.com/openharmony/multimedia_camera_standard
    // 样例:foundation/multimedia/camera_standard/interfaces/innerkits/native/test/camera_capture.cpp



    RetCode rc = RC_OK;

    auto mainDemo = std::make_shared<HosCameraDemo>();
    rc = mainDemo->InitSensors();
    if (rc == RC_ERROR) {
        CAMERA_LOGE("main test: mainDemo->InitSensors() error\n");
        return -1;
    }
    rc = mainDemo->InitCameraDevice();
    if (rc == RC_ERROR) {
        CAMERA_LOGE("main test: mainDemo->InitCameraDevice() error\n");
        return -1;
    }

    rc = PreviewOn(0, mainDemo);
    if (rc != RC_OK) {
        CAMERA_LOGE("main test: PreviewOn() error demo exit");
        return -1;
    }

    ManuList(mainDemo, argc, argv);

    return RC_OK;
}
} // namespace Camera

int main(int argc, char** argv)
{
    OHOS::Camera::main(argc, argv);

    return 0;
}
